package com.dq.wx.pay;

import com.alibaba.fastjson.JSONObject;
import com.dq.domain.subscribe.Subscribe;
import com.dq.domain.sys.user.Account;
import com.dq.exception.customException.AdminException;
import com.dq.repository.account.AccountRepository;
import com.dq.repository.user.SubscribeRepository;
import com.dq.service.OssService;
import com.dq.utils.HttpContextUtils;
import com.dq.utils.IPUtils;
import com.dq.utils.SpringUtil;
import com.dq.wx.pay.config.WxPayProperties;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxScanPayNotifyResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayOrderCloseResult;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 微信支付 utils
 * @author cf
 */
public class WxPayUtils {

    public static WxPayService wxPayService = SpringUtil.getBean(WxPayService.class);
    private static WxPayProperties wxPayProperties = SpringUtil.getBean(WxPayProperties.class);
    private static SubscribeRepository ordersRepository = SpringUtil.getBean(SubscribeRepository.class);
    private static AccountRepository accountRepository = SpringUtil.getBean(AccountRepository.class);
    private static OssService ossService = SpringUtil.getBean(OssService.class);

    /**
     * 所有支付 的 统一下单
     * 统一下单(详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1)
     * 在发起微信支付前，需要调用统一下单接口，获取"预支付交易会话标识"
     * 接口地址：https://api.mch.weixin.qq.com/pay/unifiedorder
     */
    @ApiOperation(value = "原生的统一下单接口")
    public static WxPayUnifiedOrderResult unifiedOrder(String outTradeNo, String body, Integer totalFee, String productId, String spbillCreateIp,
                                                String tradeType) throws WxPayException {
        WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
        request.setOutTradeNo(outTradeNo);
        request.setBody(body);
        if(totalFee <= 0) {
            throw new AdminException("支付金额<=0");
        }
        request.setTotalFee(totalFee);
        if (StringUtils.isNotBlank(productId)) {
            request.setProductId(productId);
        }

        if (StringUtils.isBlank(spbillCreateIp)) {
            spbillCreateIp = IPUtils.getIpAddr(HttpContextUtils.getHttpServletRequest());
        }
        request.setSpbillCreateIp(spbillCreateIp);
        request.setTradeType(tradeType);

        String notifyUrl = wxPayProperties.getNotifyUrl();
        request.setNotifyUrl(notifyUrl);

        return wxPayService.unifiedOrder(request);
    }
    @ApiOperation(value = "原生的统一下单接口 - NATIVE, 返回所有信息")
    public static WxPayUnifiedOrderResult unifiedOrder2native(String outTradeNo, String body, Integer totalFee, String productId) throws WxPayException {

        String spbillCreateIp = wxPayProperties.getSpbillCreateIp2native();
        return unifiedOrder(outTradeNo, body, totalFee, productId, spbillCreateIp, "NATIVE");
    }
    /**
     *
     * @param loginUserId 用来 [用户ID+商品ID命名]命名交易会话二维码, 保证一个用户一个商品只会有一个名字的二维码
     * @return
     */
    @ApiOperation(value = "原生的统一下单接口 - NATIVE, 生成交易会话的二维码")
    public static String unifiedOrder2native2qr(String outTradeNo, String body, Integer totalFee, String productId, Long loginUserId) throws WxPayException {
        WxPayUnifiedOrderResult result = unifiedOrder2native(outTradeNo, body, totalFee, productId);

        String sign = result.getSign();
        // 获取 二维码链接
        String codeUrl = result.getCodeURL();
        // 请求 二维码链接, 获取 byte[]
        byte[] qrcodeByte = WxPayUtils.createScanPayQrcodeMode2(codeUrl, null, null);
        InputStream in = new ByteArrayInputStream(qrcodeByte);
        // 根据返回二维码URL, 生成图片, 保存到OSS, 同时保存一些信息到订单
        String ossQrUrl = qrUrl2oss(outTradeNo, productId, loginUserId, sign, in);

        return ossQrUrl;
    }

    /**
     * 根据返回二维码URL, 生成图片, 保存到OSS, 同时保存一些信息到订单
     */
    public static String qrUrl2oss(String outTradeNo, String productId, Long loginUserId, String sign, InputStream in) {
        // 保存二维码到 OSS, TODO 这个二维码是会失效的, 在用户扫码支付后, 要在OSS中删除, 避免后续无关文件过大, 可以使用 用户ID+商品ID命名,
        if(loginUserId == null) {
            throw new AdminException("loginUserId为空");
        }
        String ossUrl = ossService.upload(in, "wxPay/qr", productId + "_" + loginUserId + ".jpg");

        if(outTradeNo.contains("Ac_")) {
            // 保存信息到 订单中
            Account orders = accountRepository.findByDeletedFalseAndOrderNo(outTradeNo);
            if(orders == null) {
                throw new RuntimeException("原生的统一下单接口 [异常]: 记录不存在,  orderNo: "+ outTradeNo);
            }
            // 保存sign, 用于回调时验证sign
            orders.setSign(sign);
            orders.setQrOssUrl(ossUrl);
            accountRepository.save(orders);
        }else {
            // 保存信息到 订单中
            Subscribe orders = ordersRepository.findByDeletedFalseAndSubscribeNo(outTradeNo);
            if(orders == null) {
                throw new RuntimeException("原生的统一下单接口 [异常]: 订单不存在,  orderNo: "+ outTradeNo);
            }
            // 保存sign, 用于回调时验证sign
            orders.setSign(sign);
            orders.setQrOssUrl(ossUrl);
            ordersRepository.save(orders);
        }

        return ossUrl;
    }

    /**
     * 扫码支付模式二生成二维码的方法
     * 对应链接格式：weixin：//wxpay/bizpayurl?sr=XXXXX。请商户调用第三方库将code_url生成二维码图片。
     * 该模式链接较短，生成的二维码打印到结账小票上的识别率较高。
     * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5
     *
     * @param codeUrl    微信返回的交易会话的二维码链接
     * @param logoFile   商户logo图片的文件对象，可以为空
     * @param sideLength 要生成的二维码的边长，如果为空，则取默认值400
     * @return 生成的二维码的字节数组
     */
    public static byte[] createScanPayQrcodeMode2(String codeUrl, File logoFile, Integer sideLength) {
        return wxPayService.createScanPayQrcodeMode2(codeUrl, logoFile, sideLength);
    }

    /**
     * 查询订单(详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2)
     * 该接口提供所有微信支付订单的查询，商户可以通过查询订单接口主动查询订单状态，完成下一步的业务逻辑。
     * 需要调用查询接口的情况：
     * ◆ 当商户后台、网络、服务器等出现异常，商户系统最终未接收到支付通知；
     * ◆ 调用支付接口后，返回系统错误或未知交易状态情况；
     * ◆ 调用被扫支付API，返回USERPAYING的状态；
     * ◆ 调用关单或撤销接口API之前，需确认支付状态；
     * 接口地址：https://api.mch.weixin.qq.com/pay/orderquery
     *
     * @param transactionId 微信订单号
     * @param outTradeNo    商户系统内部的订单号，当没提供transactionId时需要传这个。
     */
    @ApiOperation(value = "查询订单")
    public static WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxPayException {
        return wxPayService.queryOrder(transactionId, outTradeNo);
    }

    /**
     * 支付成功, 不能关单, 关闭后的订单号不能再次用来调用统一下单接口
     * @param outTradeNo 商户系统内部订单号
     */
    @ApiOperation(value = "关闭订单")
    public static WxPayOrderCloseResult closeOrder(String outTradeNo) throws WxPayException {
        if(StringUtils.isBlank(outTradeNo)) {
            throw new AdminException("outTradeNo为空");
        }
        return wxPayService.closeOrder(outTradeNo);
    }

}
